import * as h337 from "heatmap.js-fixed/build/heatmap";
import HeatmapPrimitive from "@/primitives/HeatmapPrimitive.js";
import throttle from "lodash.throttle";
import { Delaunay } from "d3-delaunay";

export default (Cesium) =>
  class CesiumHeat {
    constructor(
      viewer,
      data = {
        autoMaxMin: true,
        data: [],
      },
      bbox = [-180, -90, 180, 90],
      heatmapConfig = {},
      autoRadiusConfig = {
        enabled: true,
        throttle: 200, // ms
        min: 6375000,
        max: 10000000,
        maxRadius: 20 * 2,
        minRadius: 5 * 2,
      },
      canvasConfig = {
        totalArea: 360 * 2 * 720 * 2,
        autoResize: true,
      }
    ) {
      if (typeof window == "undefined") return;

      this.updateCesium = throttle(
        this._updateCesium,
        autoRadiusConfig.throttle || 200,
        { trailing: true }
      );

      this.viewer = viewer;
      this.bbox = bbox;
      this.autoRadiusConfig = autoRadiusConfig;
      this.max = undefined;
      this.min = undefined;

      // bbox计算基础信息
      const [left, bottom, right, top] = bbox;
      let height = top - bottom,
        width = right - left;
      this.boxMeta = {
        top,
        left,
        height,
        width,
      };

      // 计算画布大小
      if (canvasConfig.autoResize) {
        if (!canvasConfig.totalArea) {
          throw "specify totalArea if auto resize";
        }
        // w*h = totalArea
        // w:h = width/height
        const h = Math.floor(Math.sqrt(height * canvasConfig.totalArea));
        const w = Math.floor((h * width) / height);
        this.canvasConfig = {
          ...canvasConfig,
          width: w,
          height: h,
        };
      } else {
        if (!canvasConfig.width || !canvasConfig.height) {
          throw "specify width and height if not auto resize";
        }
        this.canvasConfig = canvasConfig;
      }

      // 初始化heatmap
      let config = { ...heatmapConfig };
      if (!config.container) {
        this.mountPoint = newDiv(
          {
            position: `absolute`,
            top: 0,
            left: 0,
            "z-index": -100,
            overflow: "hidden",
            width: 0,
            height: 0,
          },
          document.body
        );

        config.container = newDiv(
          {
            width: this.canvasConfig.width,
            height: this.canvasConfig.height,
          },
          this.mountPoint
        );
      }
      this.heatmapConfig = config;
      this.heatmap = h337.create(config);
      console.log("this.heatmap", this.heatmap);

      // 设置热力图数据
      let dataConfig;
      if (Array.isArray(data)) {
        dataConfig = {
          autoMaxMin: true,
          data,
        };
      } else {
        dataConfig = {
          ...data,
        };
      }
      if (!dataConfig.autoMaxMin) {
        if (!dataConfig.min || !dataConfig.max) {
          throw "need max and min when not auto";
        }
        this.min = dataConfig.min;
        this.max = dataConfig.max;
      }
      let newData = dataConfig.data.map((x) => {
        this.updateMaxMin(x.value);
        return this.convertData(x);
      });
      console.log("data", data);
      delete dataConfig.data;

      this.rawData = data;
      this.dataConfig = dataConfig;
      this.data = newData;
      let heatdata = {
        max: this.max,
        min: this.min,
        data: newData,
      };
      console.log("heatdata", heatdata);
      this.heatmap.setData(heatdata);

      // 更新到cesium
      this.updateCesium(autoRadiusConfig.enabled);
      this.cameraMoveEnd = () => this.updateCesium(true);
      this.postRender = () => {
        if (this.postRenderSkip) {
          this.postRenderSkip = false;
          return;
        }
        this.postRenderSkip = true;
        this.updateCesium(true);
      };
      if (autoRadiusConfig.enabled) {
        this.viewer.camera.moveEnd.addEventListener(this.cameraMoveEnd);
        // this.viewer.scene.postRender.addEventListener(this.postRender)
      }
    }

    /**
     * 增加一个或多个点
     * @param {Object|[]} x
     */
    addData(x) {
      if (Array.isArray(x)) {
        this.data = this.data.concat(
          x.map((y) => {
            this.updateMaxMin(y.value);
            return this.convertData(y);
          })
        );
      } else {
        this.updateMaxMin(x.value);
        this.data.push(this.convertData(x));
      }
      this.updateCesium(true);
    }

    /**
     * 按当前的相机高度调整点的辐射（越高，越大）
     */
    updateHeatmap() {
      // 获取摄像机位置的大小。在 3D 中，这是矢量大小。在 2D 和 哥伦布视图，这是到地图的距离。
      let h = this.viewer.camera.getMagnitude();
      const { min, max, minRadius, maxRadius } = this.autoRadiusConfig;
      let newRadius = parseInt(
        minRadius + ((maxRadius - minRadius) * (h - min)) / (max - min)
      );
      // console.log("newRadius", newRadius);
      this.heatmap.setData({
        max: this.max,
        min: this.min,
        data: this.data.map(({ x, y, value }) => {
          return {
            x,
            y,
            value,
            radius: newRadius,
          };
        }),
      });
    }

    /**
     * 更新cesium显示
     * @param {*} updateHeat
     */
    async _updateCesium(updateHeat) {
      if (this.layer) {
        this.viewer.scene.imageryLayers.remove(this.layer);
      }
      updateHeat && this.updateHeatmap();

      let provider = await Cesium.SingleTileImageryProvider.fromUrl(
        this.heatmap.getDataURL(),
        {
          rectangle: Cesium.Rectangle.fromDegrees(...this.bbox),
        }
      );
      // this.layer = this.viewer.scene.imageryLayers.addImageryProvider(provider);
      // const geometry = this.generateMeshData();
      // const heatmapPrimitive = new HeatmapPrimitive(
      //   this.heatmap.getDataURL(),
      //   geometry
      // );
      // this.viewer.scene.primitives.add(heatmapPrimitive);
      const appearance = new Cesium.MaterialAppearance({
        flat: true, // 平面模式
        translucent: true, // 设置透明度
        // 材质外观
        material: new Cesium.Material({
          // 定义材质
          fabric: {
            // 材质属性
            type: "Image", // 图像类型
            uniforms: {
              // 热力图
              image: this.heatmap.getDataURL(), // 热力图数据URL
              // 拉伸系数
              exaggerateNum: 10000.0,
            },
          },
        }),
        vertexShaderSource: `
        in vec3 position3DHigh;
        in vec3 position3DLow;
        in vec3 normal;
        in vec2 st;
        in float batchId;

        out vec3 v_positionEC;
        out vec3 v_normalEC;
        out vec2 v_st;

        // 变量需要加下划线和索引
        // 热力图
        uniform sampler2D image_0;
        // 拉伸系数
        uniform float exaggerateNum_1;

        void main() {
          vec4 p = czm_computePosition();
          vec4 color = texture(image_0, st);
          p = vec4(p.xyz + normal * color.a * exaggerateNum_1, 0.8);
          v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
          v_st = st;
          gl_Position = czm_modelViewProjectionRelativeToEye * p;
        }
        `,
      });
      const bounds = [
        Math.min(...this.rawData.map((d) => d.x)) + 1.90,
        Math.min(...this.rawData.map((d) => d.y)),
        Math.max(...this.rawData.map((d) => d.x)),
        Math.max(...this.rawData.map((d) => d.y)),
      ];
      console.log("bounds", bounds);
      this.viewer.scene.primitives.add(
        new Cesium.Primitive({
          geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.RectangleGeometry({
              // ellipsoid: Cesium.Ellipsoid.default,
              rectangle: Cesium.Rectangle.fromDegrees(...bounds),
              vertexFormat: Cesium.VertexFormat.POSITION_NORMAL_AND_ST,
              granularity: Cesium.Math.toRadians(0.05),
            }),
          }),
          asynchronous: false,
          appearance: appearance,
        })
      );
    }

    // 生成三维网格数据
    generateMeshData() {
      const baseElevation = 1000;
      const gridWidth = 10;
      const gridHeight = 10;
      const positions = [];
      const textureCoords = [];
      // const indices = [];
      const maxValue = 200;
      const indices = this.generateTriangulation();
      const bounds = [
        Math.min(...this.rawData.map((d) => d.x)),
        Math.min(...this.rawData.map((d) => d.y)),
        Math.max(...this.rawData.map((d) => d.x)),
        Math.max(...this.rawData.map((d) => d.y)),
      ];
      for (let i = 0; i < this.rawData.length; i++) {
        const coords = this.gps2point([this.rawData[i].x, this.rawData[i].y]);
        const heatValue = this.heatmap.getValueAt({
          x: coords[0],
          y: coords[1],
        });
        // 计算三维坐标（经纬度高程）
        const cartesian3 = Cesium.Cartesian3.fromDegrees(
          this.rawData[i].x,
          this.rawData[i].y,
          (heatValue / maxValue) * 1000
        );
        positions.push(cartesian3.x, cartesian3.y, cartesian3.z);
        // 纹理坐标映射
        textureCoords.push(
          (this.rawData[i].x - bounds[0]) / (bounds[2] - bounds[0]),
          (this.rawData[i].y - bounds[1]) / (bounds[3] - bounds[1])
        );
      }

      // 遍历网格点生成顶点数据
      // for (let i = 0; i < gridWidth; i++) {
      //   for (let j = 0; j < gridHeight; j++) {
      //     const heatValue = this.heatmap.getValueAt({ x: 789, y: 165 });
      //     // console.log("heatValue", heatValue)
      //     // 计算三维坐标（经纬度高程）
      //     const cartesian3 = Cesium.Cartesian3.fromDegrees(
      //       120 + i / 10,
      //       30 + j / 10,
      //       Math.ceil(Math.random() * 10000)
      //     );
      //     positions.push(cartesian3.x, cartesian3.y, cartesian3.z);
      //     textureCoords.push(i / gridWidth, j / gridHeight);
      //   }
      // }
      // 生成三角网格索引
      // for (let i = 0; i < gridWidth - 1; i++) {
      //   for (let j = 0; j < gridHeight - 1; j++) {
      //     const topLeft = i * gridHeight + j;
      //     const topRight = topLeft + 1;
      //     const bottomLeft = (i + 1) * gridHeight + j;
      //     const bottomRight = bottomLeft + 1;

      //     // 第一个三角形
      //     indices.push(topLeft, bottomLeft, topRight);
      //     // 第二个三角形
      //     indices.push(topRight, bottomLeft, bottomRight);
      //   }
      // }
      return new Cesium.Geometry({
        attributes: {
          position: new Cesium.GeometryAttribute({
            componentDatatype: Cesium.ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: positions,
          }),
          st: new Cesium.GeometryAttribute({
            // 新增
            componentDatatype: Cesium.ComponentDatatype.FLOAT,
            componentsPerAttribute: 2,
            values: new Float32Array(textureCoords),
          }),
        },
        // indices: new Uint16Array(indices),
        primitiveType: Cesium.PrimitiveType.TRIANGLES,
        // 定义包围球
        boundingSphere: Cesium.BoundingSphere.fromVertices(positions),
      });
    }

    /**
     * 转换坐标
     * @param {*} param0
     */
    convertData({ x, y, value }) {
      let [px, py] = this.gps2point([x, y]);
      return {
        x: px,
        y: py,
        value: value,
      };
    }

    /**
     * 更新最大值最小值
     * @param {number} value
     */
    updateMaxMin(value) {
      if (this.max === undefined) {
        this.max = value;
      } else {
        this.max = Math.max(value, this.max);
      }
      if (this.min === undefined) {
        this.min = value;
      } else {
        this.min = Math.min(value, this.min);
      }
    }

    gps2point(gps = []) {
      let [x1, y1] = gps;
      let { top, left, height, width } = this.boxMeta;
      let canvasConfig = this.canvasConfig;

      let x = parseInt(((x1 - left) / width) * canvasConfig.width);
      let y = parseInt(((top - y1) / height) * canvasConfig.height);
      return [x, y];
    }

    // 使用d3-delaunay库进行三角剖分
    generateTriangulation() {
      // 提取经纬度坐标
      const points = this.rawData.map(({ x, y }) => [x, y]).flat();

      // 创建Delaunay三角剖分
      const dt = new Delaunay(new Float64Array(points));
      console.log("dt", dt);
      const triangles = dt.triangles;

      // 生成索引数组
      const indices = [];
      for (let i = 0; i < triangles.length; i += 3) {
        indices.push(triangles[i], triangles[i + 1], triangles[i + 2]);
      }
      return indices;
    }

    /**
     * 销毁
     */
    destory() {
      if (this.autoRadiusConfig.enabled) {
        this.viewer.camera.moveEnd.removeEventListener(this.cameraMoveEnd);
        // this.viewer.scene.postRender.removeEventListener(this.postRender)
      }
      if (this.layer) {
        this.viewer.scene.imageryLayers.remove(this.layer);
      }
      if (this.mountPoint) {
        this.mountPoint.remove();
      }
    }

    /**
     * 世界坐标数组转经纬度数组
     * @param {Cesium.Cartesian3[]} cartesians 世界坐标数组
     * @param {Cesium.Viewer} viewer 当前viewer对象
     * @returns { Array } 经纬度坐标数组
     */
    cartesiansToLnglats(cartesians, viewer) {
      if (!cartesians || cartesians.length < 1) return;
      viewer = viewer || window.viewer;
      if (!viewer) {
        console.log("util.cartesiansToLnglats方法缺少viewer对象");
        return;
      }
      var arr = [];
      for (var i = 0; i < cartesians.length; i++) {
        arr.push(this.cartesianToLnglat(cartesians[i], viewer));
      }
      return arr;
    }

    /**
     * 世界坐标转经纬度
     * @param {Cesium.Cartesian3 } cartesian 世界坐标
     * @param {Cesium.Viewer} viewer 当前viewer对象
     * @returns { Array } 经纬度坐标s
     */
    cartesianToLnglat(cartesian, viewer) {
      if (!cartesian) return [];
      viewer = viewer || window.viewer;
      var lnglat = Cesium.Cartographic.fromCartesian(cartesian);
      var lat = Cesium.Math.toDegrees(lnglat.latitude);
      var lng = Cesium.Math.toDegrees(lnglat.longitude);
      var hei = lnglat.height;
      return [lng, lat, hei];
    }
  };

/**
 * 创建一个标签
 * @param {*} style
 * @param {*} parent
 */
function newDiv(style, parent) {
  let div = document.createElement("div");
  parent && parent.append(div);
  for (let k in style) {
    if (typeof style[k] === "number") {
      div.style[k] = style[k] + "px";
      continue;
    }
    div.style[k] = style[k];
  }
  return div;
}
